#include "GameManager.hpp"
#include <GL\freeglut.h>
#include "siut/simd/Vec3f.hpp"
#include "siut/simd/Vec4f.hpp"
#include "siut/simd/Mat4f.hpp"

using siut::simd::Vec3f;
using siut::simd::Vec4f;
using siut::simd::Mat4f;

GameManager::GameManager()
{
  gamePlayTime_ = 0.0;
  minionSpawnRate_ = 3.0;
  minionSpawnCountdown_ = minionSpawnRate_;
  minionsKilled_ = 0;
  lives_ = 10;
}

GameManager::~GameManager()
{
}

void GameManager::privateInit()
{
  // Set default OpenGL states
  glEnable(GL_CULL_FACE);

  // Adding the camera to the scene
  cam_.reset(new Camera());
  //  this->addSubObject(cam_);
  //  matrix_.translate(0.0f, 0.0f, -99.0f);

  bf_.reset(new BattleField());
  this->addSubObject(bf_);

  int maxMinions = 10;
  for(int i=0; i<maxMinions; i++)
	minions_.push_back( boost::shared_ptr<Minion>(new Minion()) );
  
  for(unsigned int i=0; i<minions_.size(); i++)
  {
	this->addSubObject(minions_[i]);
  }

  int maxBullets = 1000;
  for(int i=0; i < maxBullets; i++)
	bullets_.push_back( boost::shared_ptr<Bullet>(new Bullet()) );

  for(unsigned int i=0; i<bullets_.size(); i++)
  {
	this->addSubObject(bullets_[i]);
  }

  makeWaypointPath();
}

void GameManager::privateRender()
{
  drawMinionPath();

  	//Set matrices for ortho
	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();
	gluOrtho2D(-1.0f, 1.0f, -1.0f, 1.0f); // left right top bottom

  // Nothing to render
  	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity();

	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE);

	glPushMatrix();
	glTranslatef(0,1.7f,0);
	glColor4f(.7f, .7f, .7f, .9f);
	glBegin(GL_QUADS);
	  glVertex3f(-1.0f, -1.0f, -.1f);
	  glVertex3f(-0.5f, -1.0f, -.1f);
	  glVertex3f(-0.5f, -0.7f, -.1f);
	  glVertex3f(-1.0f, -0.7f, -.1f);
	glEnd();
	glPopMatrix();

	glColor4f(1, 1, 0, 1.0f);

	glRasterPos2f(-1.0f, 0.95f);

	//Print fps
	static char fpsString[32];
	sprintf_s(fpsString, "%.2f playtime", gamePlayTime_);

	for(unsigned int i=0; i<strlen(fpsString); ++i)
		glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18, fpsString[i]);
	
	float hang, vang;
	cam_->getRot(hang, vang);

	//Print camera angle
	glRasterPos2f(-1.0f, 0.90f);
	static char angString[32];
	sprintf_s(angString, "camera angles: %.2fh %.2fv", hang, vang);

	for(unsigned int i=0; i<strlen(angString); ++i)
	  glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18, angString[i]);
	
	//Print stats
	glRasterPos2f(-1.0f, 0.85f);
	static char statString[32];
	sprintf_s(statString, "%i towers", towers_.size() );

	for(unsigned int i=0; i<strlen(statString); ++i)
	  glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18, statString[i]);
	
	//Print coords
	glRasterPos2f(-1.0f, 0.80f);
	sprintf_s(statString, "%i %i grid", bf_->gridX, bf_->gridY );

	for(unsigned int i=0; i<strlen(statString); ++i)
	  glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18, statString[i]);	

	//Print spawntimer
	glRasterPos2f(-1.0f, 0.75f);
	sprintf_s(statString, "%.2fs to spawn", minionSpawnCountdown_ );

	for(unsigned int i=0; i<strlen(statString); ++i)
	  glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18, statString[i]);	

	// print num minions killed
	glRasterPos2f(-1.0f, 0.70f);
	sprintf_s(statString, "%i minions killed", minionsKilled_ );

	for(unsigned int i=0; i<strlen(statString); ++i)
	  glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18, statString[i]);	
	
	// print num minions killed
	glRasterPos2f(-1.0f, 0.65f);
	sprintf_s(statString, "%i lives", lives_ );

	for(unsigned int i=0; i<strlen(statString); ++i)
	  glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18, statString[i]);	

	glDisable(GL_BLEND);

	//reset matrices
	glMatrixMode(GL_PROJECTION);
	glPopMatrix();
	glMatrixMode(GL_MODELVIEW);
	glPopMatrix();
}

void GameManager::privateUpdate()
{
  gamePlayTime_ += sslf_;
  minionSpawnCountdown_ -= sslf_;
  if( minionSpawnCountdown_ <= 0.0 )
  {
	minionSpawnCountdown_ = minionSpawnRate_; // reset countdown
	spawnMinion();
  }



  // Instead of adding all objects in the scene as subobject to the camera they are added as subobjects
  // to the game manager. Therefore, we set the game manager matrix equal to the camera matrix. 
  this->matrix_ = cam_->getMatrix();
  cam_->update(sslf_);

  for(unsigned int i=0; i < towers_.size(); i++)
  {
	for(unsigned int j=0; j < minions_.size(); j++)
	{
	  if(minions_[j]->isAlive())
	  {
		if( towers_[i]->isInShotRange( minions_[j] ) )
		{
		  Vec3f bulletDir = minions_[j]->getPos() -  towers_[i]->getPos();
		  bulletDir.normalize();
		  makeBullet( bulletDir, towers_[i]->getPos() );
		  continue;
		}

	  }
	}
  }

  // Bullet collision check
 for(unsigned int i=0; i < bullets_.size(); i++)
  {
	if( bullets_[i]->isAlive() )
	{
	  for(unsigned int j=0; j < minions_.size(); j++)
	  {
		if(minions_[j]->isAlive() )
		{
		  float distSq = euclideanDistanceSquared( minions_[j]->getPos(), bullets_[i]->getPos() );
		  if( distSq < 3.0f*3.0f + 5.0f*5.0f ) // bulletRadiusSquared + minionRadiusSquared
		  {
			minions_[j]->takeDamage();
			
			if( minions_[j]->isDead() ) 
			  minionsKilled_++;
			
			bullets_[i]->setDead();
			continue;
		  }
		}
	  }
	}
  }
}

void GameManager::makeBullet(const Vec3f& bulletDir, const Vec3f& towerPos)
{
  for(unsigned int i=0; i < bullets_.size(); i++)
  {
	if( bullets_[i]->isDead() )
	{
	  bullets_[i]->setAlive();
	  bullets_[i]->setDir( bulletDir );
	  bullets_[i]->setPosition( towerPos );
	  return;
	}
  }
}

boost::shared_ptr<Camera> GameManager::getCam()
{
  return cam_;
}

boost::shared_ptr<BattleField> GameManager::getBattleField()
{
  return bf_;
}

void GameManager::placeTower()
{
  if( bf_->pickedPosEmpty() )
  {
	Vec3f pick( *bf_->getPickedPos() );
	boost::shared_ptr<Tower> t( new Tower() );
	Mat4f oldPos = t->getMatrix();
	translate(oldPos , pick[0], pick[1]+5.1f, pick[2] );
	t->setMatrix(oldPos);

	this->addSubObject(t);
	towers_.push_back(t);

	bf_->setActiveTileAsTower();
	
  }

}

void GameManager::spawnMinion()
{
  for(unsigned int i=0; i<minions_.size(); i++)
  {
	if( minions_[i]->isDead() )
	{
	  minions_[i]->setWaypoints( waypointsReal_ );
	  minions_[i]->setAlive();
	  return;
	}
  }
}

void GameManager::drawMinionPath()
{
  for(unsigned int i=0; i<waypointTiles_.size()-1; i++)
  {
	int waypointX1 = waypointTiles_[i].first;
	int waypointY1 = waypointTiles_[i].second;

	int waypointX2 = waypointTiles_[i+1].first;
	int waypointY2 = waypointTiles_[i+1].second;

	int diffx = waypointX2 - waypointX1;
	int diffy = waypointY2 - waypointY1;

	int dirx = 0;
	if(diffx < 0) dirx = -1;
	if(diffx > 0) dirx = 1;

	int diry = 0;
	if(diffy < 0) diry = -1;
	if(diffy > 0) diry = 1;

	for(int xwalk=0; xwalk < abs(diffx); xwalk++)
	{
	  float gridPosX;
	  float gridPosY;
	  bf_->getRealCoordFromInteger(waypointX1 + dirx * xwalk, waypointY1, gridPosX, gridPosY);
	  float xscale;
	  float yscale;
	  bf_->getScale(xscale,yscale);

	  glColor3f(0,0,1);
	  glPushMatrix();
		glTranslatef( gridPosX, 0.8f, gridPosY );

		glBegin(GL_QUADS);
		glVertex3f(-xscale/2,0,-yscale/2);
		glVertex3f(-xscale/2,0,yscale/2);
		glVertex3f(xscale/2,0,yscale/2);
		glVertex3f(xscale/2,0,-yscale/2);

		glEnd();

	  glPopMatrix();
	}

	for(int ywalk=0; ywalk < abs(diffy); ywalk++)
	{
	  float gridPosX;
	  float gridPosY;
	  bf_->getRealCoordFromInteger(waypointX1, waypointY1 + diry * ywalk, gridPosX, gridPosY);
	  float xscale;
	  float yscale;
	  bf_->getScale(xscale,yscale);

	  glColor3f(0,0,1);
	  glPushMatrix();
		glTranslatef( gridPosX, 0.8f, gridPosY );

		glBegin(GL_QUADS);
		glVertex3f(-xscale/2,0,-yscale/2);
		glVertex3f(-xscale/2,0,yscale/2);
		glVertex3f(xscale/2,0,yscale/2);
		glVertex3f(xscale/2,0,-yscale/2);

		glEnd();

	  glPopMatrix();
	}
  }
}

void GameManager::makeWaypointPath()
{
  assert( bf_ != 0 );

  // Define minion waypoint path
  waypointTiles_.push_back( std::pair<int,int>(0,19) );
  waypointTiles_.push_back( std::pair<int,int>(10,19) );
  waypointTiles_.push_back( std::pair<int,int>(10,16) );
  waypointTiles_.push_back( std::pair<int,int>(1,16) );
  waypointTiles_.push_back( std::pair<int,int>(1,11) );
  waypointTiles_.push_back( std::pair<int,int>(16,11) );
  waypointTiles_.push_back( std::pair<int,int>(16,1) );
  waypointTiles_.push_back( std::pair<int,int>(18,1) );
  waypointTiles_.push_back( std::pair<int,int>(18,0) );
  waypointTiles_.push_back( std::pair<int,int>(19,0) );

  // Tell Battlefield waypoint tiles are illegal
  for(unsigned int i=0; i<waypointTiles_.size()-1; i++)
  {
	int waypointX1 = waypointTiles_[i].first;
	int waypointY1 = waypointTiles_[i].second;

	int waypointX2 = waypointTiles_[i+1].first;
	int waypointY2 = waypointTiles_[i+1].second;

	int diffx = waypointX2 - waypointX1;
	int diffy = waypointY2 - waypointY1;

	int dirx = 0;
	if(diffx < 0) dirx = -1;
	if(diffx > 0) dirx = 1;

	int diry = 0;
	if(diffy < 0) diry = -1;
	if(diffy > 0) diry = 1;

	for(int xwalk=0; xwalk < abs(diffx); xwalk++)
	{
	  bf_->setTileToPath(waypointX1 + dirx * xwalk, waypointY1);
	}

	for(int ywalk=0; ywalk < abs(diffy); ywalk++)
	{
	  bf_->setTileToPath(waypointX1, waypointY1 + diry * ywalk);
	}
  }

  // Convert integer tilecoords [0, boardSiz] to continous realworld coords.
  for(unsigned int i=0; i<waypointTiles_.size(); i++)
  {
	int getX = waypointTiles_[i].first;
	int getY = waypointTiles_[i].second;
	float gridPosX, gridPosY;
	bf_->getRealCoordFromInteger(getX, getY, gridPosX, gridPosY); 
	waypointsReal_.push_back( boost::shared_ptr<Vec3f>( new Vec3f(gridPosX, 5.0f, gridPosY) ) );
  }
}